package windows;

import interfaces.CollectionView;
import interfaces.Controller;

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Container;
import java.awt.FlowLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collection;

import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;

import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.ServiceReference;

import listeners.AttributeObjectButton;
import listeners.InvokeMethodButton;

public class Window {
	//CollectionView
	private CollectionView collectionView = new DefaultCollectionView();
	
	// Controller
	private Controller controller;
	
	// Window
	private JFrame window = new JFrame();
	private Container container;
	private JFrame previousWindow;

	// Lists
	private ArrayList<Component> componentsOtherAttributes;
	private ArrayList<Component> componentsListAttributes;
	private ArrayList<Component> componentsMethods;

	// Panels
	private JPanel panelLeftSide = new JPanel(new GridLayout(2, 1));
	private JPanel panelSuperiorLeftSide = new JPanel();
	private JPanel panelInferiorLeftSide = new JPanel();
	private JPanel panelCentral = new JPanel();
	private JPanel panelSouth = new JPanel();

	// ScrollPanels
	private JScrollPane scrollPaneCentral = new JScrollPane(panelCentral);
	private JScrollPane scrollPaneInferiorLeftSide = new JScrollPane(panelInferiorLeftSide);
	private JScrollPane scrollPaneSuperiorLeftSide = new JScrollPane(panelSuperiorLeftSide);
	
	public Window(Object obj, Controller controller) {
		this.controller = controller;
		Class<?> c = obj.getClass();
		window.setTitle(c.getSimpleName());
		window.setLocation(400, 200);
		container = window.getContentPane();
		componentsOtherAttributes = new ArrayList<Component>();
		componentsMethods = new ArrayList<Component>();
		componentsListAttributes = new ArrayList<Component>();
		attributeToLabels(c, obj);
		windowFormat();
	}
	
	public Window(Object obj, JFrame previousWindow, Controller controller) {
		this.controller = controller;
		this.previousWindow = previousWindow;
		window.setLocation(previousWindow.getLocation());
		Class<?> c = obj.getClass();
		window.setTitle(c.getSimpleName());
		container = window.getContentPane();
		componentsOtherAttributes = new ArrayList<Component>();
		componentsMethods = new ArrayList<Component>();
		componentsListAttributes = new ArrayList<Component>();
		attributeToLabels(c, obj);
		windowFormat();
	}

	public void windowFormat() {
		container.setLayout(new BorderLayout());
		panelCentral.setLayout(new BoxLayout(panelCentral, BoxLayout.Y_AXIS));
		panelSuperiorLeftSide.setLayout(new BoxLayout(panelSuperiorLeftSide, BoxLayout.Y_AXIS));
		panelSouth.setLayout(new FlowLayout());
		panelLeftSide.add(scrollPaneSuperiorLeftSide);
		panelLeftSide.add(scrollPaneInferiorLeftSide);
		container.add(panelLeftSide, BorderLayout.WEST);
		container.add(scrollPaneCentral, BorderLayout.CENTER);
		container.add(panelSouth, BorderLayout.SOUTH);
		window.setSize(500, 400);
		window.setVisible(true);
		window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
	}

	public void attributeToLabels(Class<?> c, Object obj) {
		int otherAttributes = 0;
		for (Field f : c.getDeclaredFields()) {
			try {
				f.setAccessible(true);
				Object object = f.get(obj);
				if (f.getType().isPrimitive() || f.getType().equals(String.class)) {
					otherAttributes++;
					componentsOtherAttributes.add(new JLabel(f.getName()));
					if (f.getType().equals(boolean.class)) {
						JCheckBox checkBox = new JCheckBox("", (boolean) object);
						checkBox.setEnabled(false);
						componentsOtherAttributes.add(checkBox);
					} else {
						componentsOtherAttributes.add(new JLabel(object.toString()));
					}
				} else if (Collection.class.isAssignableFrom(f.getType())) {
					JButton buttonVerLista = new JButton(f.getName());
					buttonVerLista.addActionListener(new ButtonVerListaListener((Collection<?>) object));
					buttonVerLista.addMouseListener(new PopClickListener());
					componentsListAttributes.add(buttonVerLista);
				}else{
					otherAttributes++;
					componentsOtherAttributes.add(new JLabel(f.getName()));
					if(object != null) {
						JButton button = new JButton(object.toString());
						button.addActionListener(new AttributeObjectButton(object, window, controller));
						componentsOtherAttributes.add(button);
					} else {
						componentsOtherAttributes.add(new JLabel("null"));
					}
				}
			} catch (IllegalArgumentException | IllegalAccessException e) {
				e.printStackTrace();
			}
		}
		for (Method m : c.getDeclaredMethods()) {
			Type type = m.getGenericReturnType();
			if(type.equals(void.class)){
				JButton button = new JButton(m.getName());
				button.addActionListener(new InvokeMethodButton(obj, m, window));
				componentsMethods.add(button);
			}
		}
		init(otherAttributes, 2);
	}

	public void init(int nFields, int nCol) {
		panelInferiorLeftSide.setLayout(new GridLayout(nFields, nCol));
		panelInferiorLeftSide.setBorder(BorderFactory.createEmptyBorder(2,2,2,2));
		for (Component comp : componentsOtherAttributes) {
			panelInferiorLeftSide.add(comp);
		}
		for (Component comp : componentsListAttributes) {
			panelSuperiorLeftSide.add(comp);
		}
		for (Component comp : componentsMethods) {
			panelSouth.add(comp);
		}
		JButton exitButton = new JButton("Exit");
		exitButton.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent arg0) {
				window.dispose();
				System.exit(0);
			}
		});
		if(previousWindow!=null){
			JButton backButton = new JButton("< Back");
			backButton.addActionListener(new ActionListener() {
				@Override
				public void actionPerformed(ActionEvent arg0) {
					previousWindow.setLocation(window.getLocation());
					window.dispose();
					previousWindow.setVisible(true);
				}
			});
			panelSouth.add(backButton);
		}
		panelSouth.add(exitButton);
	}

	class PopUpMenu extends JPopupMenu {
		private static final long serialVersionUID = 1L;
		private JMenuItem menuItem;
	   
		public PopUpMenu(){
	    	menuItem = new JMenuItem(DefaultCollectionView.class.getSimpleName());
	        menuItem.addActionListener(new ActionListener() {
				
				@Override
				public void actionPerformed(ActionEvent arg0) {
					try {
						collectionView = DefaultCollectionView.class.newInstance();
					} catch (InstantiationException | IllegalAccessException e) {
						e.printStackTrace();
					}
				}
			});
	        add(menuItem);
			ServiceReference<?>[] serviceReferences;
			try {
				serviceReferences = controller.getBundleContext().getAllServiceReferences(CollectionView.class.getName(), null);
				for (ServiceReference<?> serviceReference : serviceReferences) {
					System.out.println("Service: " + serviceReference);
					final Object service = controller.getBundleContext().getService(serviceReference);
					System.out.println("Object: " + service);
					menuItem = new JMenuItem(service.getClass().getSimpleName());
			        menuItem.addActionListener(new ActionListener() {
						
						@Override
						public void actionPerformed(ActionEvent arg0) {
							collectionView = (CollectionView) service;
						}
					});
			        add(menuItem);
				}
			} catch (InvalidSyntaxException e) {
				e.printStackTrace();
			}
	    }
	}
	
	class PopClickListener extends MouseAdapter {
	    public void mousePressed(MouseEvent e){
	        if (e.isPopupTrigger())
	            doPop(e);
	    }

	    public void mouseReleased(MouseEvent e){
	        if (e.isPopupTrigger())
	            doPop(e);
	    }

	    private void doPop(MouseEvent e){
	        PopUpMenu menu = new PopUpMenu();
	        menu.show(e.getComponent(), e.getX(), e.getY());
	    }
	}
	class ButtonVerListaListener implements ActionListener {
		private Collection<?> collection;
		
		public ButtonVerListaListener(Collection<?> collection) {
			this.collection = collection;
		}

		@Override
		public void actionPerformed(ActionEvent e) {
			panelCentral.removeAll();
			panelCentral.add(collectionView.getView(collection, window, controller));
			window.validate();
			window.repaint();
		}
	}
	
	class ClicarLabel implements MouseListener{
		private Object object;
		
		public ClicarLabel(Object object){
			this.object = object;
		}
		
		@Override
		public void mouseClicked(MouseEvent arg0) {
			window.setVisible(false);
			if(controller.gotCollections(object)){
				new Window(object, window, controller);
			}else{
				new ObjectWindow(object, window, controller);
			}
		}

		@Override
		public void mouseEntered(MouseEvent arg0) {
		}

		@Override
		public void mouseExited(MouseEvent arg0) {
		}

		@Override
		public void mousePressed(MouseEvent arg0) {
		}

		@Override
		public void mouseReleased(MouseEvent arg0) {
		}
	}
}